package com.ym521.logdog.engine

import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.util.Log
import com.ym521.logdog.core.DefaultLogDogConfig
import com.ym521.logdog.core.IDiskLogEngine
import com.ym521.logdog.utils.AESecurityUtil
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*

/**
 * @author YM
 * @E-mail 2435970206@qq
 * @QQ 2435970206
 * 日志文件写入实现
 * 通过实现使用APP 私有空间 减少对用户的权限索取
 */
internal class DiskLogEngine(looper: Looper, private val mContext: Context) : Handler(looper),
    IDiskLogEngine {
    private val TAG = DiskLogEngine::class.simpleName
    private val dateFormatFile = SimpleDateFormat("yyyy-MM-dd")
    private val dateFormatMessage = SimpleDateFormat("yyyy:MM:dd HH:mm:ss.SSS")

    //普通Log文件对象
    private var logFile: File? = null

    //Crashlog文件对象
    private var crashLogFile: File? = null

    //普通Log当前日期
    private var currentLogDate: String? = null

    //CrashLog当前日期
    private var currentCrashLogDate: String? = null

    companion object {
        const val LOG = 0x10  //普通日志文件
        const val CRASH = 0x20 //普通Crash日志文件
        fun build(mContext: Context): IDiskLogEngine {
            val handlerThread = HandlerThread("DiskLogEngineThread")
            handlerThread.start()
            return DiskLogEngine(handlerThread.looper, mContext)
        }
    }

    /**
     * 接收Message
     */
    override fun handleMessage(msg: Message) {
        val logs = msg.obj as String
        when (msg.what) {
            LOG -> writeLogFile(logs)
            CRASH -> writeCrashLogFile(logs)
        }
    }

    /**
     *写入 Log日志文件
     */
    private fun writeLogFile(logMsg: String) {
        val currentDate = dateFormatFile.format(Calendar.getInstance().time)
        //如果 第一次打印或者 不是同一天 都需要新建log文件
        if (this.currentLogDate == null || currentDate != this.currentLogDate) {
            val fileDir = if (DefaultLogDogConfig.logFileShow)
                mContext.getExternalFilesDir("logdog")!!.absolutePath
            else
                mContext.getDir("logdog", Context.MODE_PRIVATE).absolutePath
            val fileName = "${currentDate}.log"
            logFile = File(fileDir, fileName)
            this.currentLogDate = currentDate
        }
        logFile?.let {
            var buff: BufferedOutputStream? = null
            try {
                if (!it.exists()) {
                    it.parentFile?.mkdirs()
                    it.createNewFile()
                }
                buff = BufferedOutputStream(FileOutputStream(it, true))
                if (DefaultLogDogConfig.encoding) {
                    buff.write(encryptionString(logMsg))
                } else {
                    buff.write(logMsg.toByteArray(Charsets.UTF_8))
                }
            } catch (e: IOException) {
                Log.e(TAG, e.toString())
            } finally {
                buff?.flush()
                buff?.close()
            }
        }
    }

    private fun encryptionString(content: String): ByteArray {
        val byteArray = content.toByteArray(Charsets.UTF_8)
        for (index in byteArray.indices) {
            byteArray[index] = encryptionByte(byteArray[index])
        }
        return byteArray
    }

    private fun encryptionByte(code: Byte): Byte {
        val ic = code.toInt() and 0xFF
        val l4 = ic shr 4
        val r4: Int = ic and 0x0F shl 4
        val c8 = l4 or r4
        return c8.toByte()
    }

    /**
     * 写入 Crash日志文件
     */
    private fun writeCrashLogFile(logMsg: String) {
        val currentDate = dateFormatFile.format(Calendar.getInstance().time)
        //如果 第一次打印或者 不是同一天 都需要新建log文件
        if (this.currentCrashLogDate == null || currentDate != this.currentCrashLogDate) {
            val fileDir = if (DefaultLogDogConfig.logFileShow)
                mContext.getExternalFilesDir("logdog")!!.absolutePath
            else
                mContext.getDir("logdog", Context.MODE_PRIVATE).absolutePath
            val fileName = "crash_${currentDate}.log"
            crashLogFile = File(fileDir, fileName)
            this.currentCrashLogDate = currentDate
        }
        crashLogFile?.let {
            var buff: BufferedOutputStream? = null
            try {
                if (!it.exists()) {
                    it.parentFile?.mkdirs()
                    it.createNewFile()
                }
                buff = BufferedOutputStream(FileOutputStream(it, true))
                if (DefaultLogDogConfig.encoding) {
                    buff.write(encryptionString(logMsg))
                } else {
                    buff.write(logMsg.toByteArray(Charsets.UTF_8))
                }
            } catch (e: IOException) {
                Log.e(TAG, e.toString())
            } finally {
                buff?.flush()
                buff?.close()
            }
        }
    }

    /**
     * 对外提供写入信息的接口
     */
    override fun logWrite(priority: Int, tag: String, msg: String) {
        val msgContent =
            if (DefaultLogDogConfig.isLogFileAES && DefaultLogDogConfig.logFileAES_key.isNotBlank()) {
                val keyBytes = DefaultLogDogConfig.logFileAES_key.toByteArray(Charsets.UTF_8)
                AESecurityUtil.encrypt(msg, keyBytes)
            } else {
                msg
            }
        val message = this.obtainMessage()
        val dateTiem = dateFormatMessage.format(Calendar.getInstance().time)
        message.obj = when (priority) {
            LogDogEngine.DEBUG -> "DEBUG    $dateTiem    $tag    $msgContent \n"
            LogDogEngine.INFO -> "INFO    $dateTiem    $tag    $msgContent \n"
            LogDogEngine.WARN -> "WARN     $dateTiem    $tag    $msgContent \n"
            LogDogEngine.ERROR, LogDogEngine.CRASH -> "ERROR    $dateTiem    $tag    $msgContent \n"
            else -> "VERBOSE   $dateTiem    $tag    $msgContent \n"
        }
        message.what = when (priority) {
            LogDogEngine.CRASH -> CRASH
            else -> LOG
        }
        this.sendMessage(message)
    }
}